package hyl.base.safe;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;

import hyl.core.MyFun;

public class MySm2 {

	private static final String SM2_STD = "sm2p256v1";
	// SM2算法使用的向量值，Java默认为1234567812345678，C#默认为null（和java的默认值效果相同），修改向量值将对算法结果存在影响
	protected static final byte[] SM2_PID = Strings.toByteArray("1234567812345678");
	static final SM2Engine.Mode DEFMODE = SM2Engine.Mode.C1C3C2;

	/**
	 * SM2加密算法
	 *
	 * @param publicKey 公钥
	 * @param data      数据
	 * @return
	 */
	public static String do公钥加密(String publicKey, String data) throws InvalidCipherTextException {

		ECPublicKeyParameters publicKeyParameters = load公钥(publicKey);
		SM2Engine sm2Engine = new SM2Engine(DEFMODE);

		sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
		byte[] arrayOfBytes = null;
		byte[] cipherDataByte = data.getBytes(StandardCharsets.UTF_8);
		arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
		String s = Hex.toHexString(arrayOfBytes);
		if (s.startsWith("04"))
			return s.substring(2);
		else
			return s;
	}

	/**
	 * SM2解密算法
	 *
	 * @param privateKey 私钥
	 * @param data       密文数据
	 * @return
	 */
	public static String do私钥解密(String privateKey, String data) throws InvalidCipherTextException {
		// 获取一条SM2曲线参数
		ECPrivateKeyParameters privateKeyParameters = load私钥(privateKey);

		if (!data.startsWith("04"))
			data = "04" + data;

		try {
			SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
			sm2Engine.init(false, privateKeyParameters);
			byte[] cipherDataByte = Hex.decode(data);
			byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
			return new String(arrayOfBytes, StandardCharsets.UTF_8);
		} catch (InvalidCipherTextException e) {

			SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C2C3);
			sm2Engine.init(false, privateKeyParameters);
			byte[] cipherDataByte = Hex.decode(data);
			byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
			return new String(arrayOfBytes, StandardCharsets.UTF_8);
		}
	}

	/**
	 * 私钥签名 1.64 模拟 1.57
	 * 
	 * @param privateKey 私钥
	 * @param content    待签名内容
	 * @return
	 */
	public static String do私钥签名(String content, String privateKey) {
		// 待签名内容转为字节数组
		byte[] message = content.getBytes();
		ECDomainParameters 曲线 = g生成曲线();
		int curveLen = getCurveLength(曲线);
		ECPrivateKeyParameters privateKeyParameters = load私钥(privateKey, 曲线);

		// 创建签名实例
		SM2Signer sm2Signer = new SM2Signer();

		// 初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
		try {
			sm2Signer.init(true, new ParametersWithID(
					new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), SM2_PID));
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		sm2Signer.update(message, 0, message.length);
		// 生成签名,签名分为两部分r和s,分别对应索引0和1的数组
		byte[] signBytes = new byte[0];
		try {
			signBytes = sm2Signer.generateSignature();
		} catch (CryptoException e) {
			throw new RuntimeException(e);
		}
		// start bc1.57版本中，signData是纯r+s字符串拼接，如果为了兼容低版本的bc包，则需要加这一句

		byte[] signData = do签名DER解码(curveLen, signBytes);
		// end
		String sign = Hex.toHexString(signData);
		return sign;
	}

	/**
	 * 私钥签名 DER
	 * 
	 * @param privateKey 私钥
	 * @param content    待签名内容
	 * @return
	 */
	public static String do私钥签名Der(String privateKey, String content) throws CryptoException {
		// 待签名内容转为字节数组
		byte[] message = content.getBytes();
		ECPrivateKeyParameters privateKeyParameters = load私钥(privateKey);
		// 创建签名实例
		SM2Signer sm2Signer = new SM2Signer();
		// 初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
		try {
			sm2Signer.init(true, new ParametersWithID(
					new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), SM2_PID));
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		sm2Signer.update(message, 0, message.length);
		// 生成签名,签名分为两部分r和s,分别对应索引0和1的数组
		byte[] signBytes = sm2Signer.generateSignature();
		String sign = Hex.toHexString(signBytes);
		return sign;
	}

	/**
	 * 验证签名
	 * 
	 * @param publicKey 公钥
	 * @param content   待签名内容
	 * @param sign      签名值
	 * @return
	 */
	public static boolean do验证签名(String content, String publicKey, String sign) {
		// 待签名内容
		byte[] message = Hex.decode(Hex.toHexString(content.getBytes()));
		byte[] signData = Hex.decode(sign);
		try {
			signData = do签名DER编码(signData);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		ECPublicKeyParameters publicKeyParameters = load公钥(publicKey);
		// 创建签名实例
		SM2Signer sm2Signer = new SM2Signer();
		ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, SM2_PID);
		sm2Signer.init(false, parametersWithID);
		sm2Signer.update(message, 0, message.length);
		// 验证签名结果
		boolean verify = sm2Signer.verifySignature(signData);
		return verify;
	}

	/**
	 * 验证签名
	 * 
	 * @param publicKey 公钥
	 * @param content   待签名内容
	 * @param sign      签名值
	 * @return
	 */
	public static boolean do验证签名Der(String content, String publicKey, String sign) {
		// 待签名内容
		byte[] message = Hex.decode(content);
		byte[] signData = Hex.decode(sign);
		ECPublicKeyParameters publicKeyParameters = load公钥(publicKey);
		ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, SM2_PID);
		// 创建签名实例
		SM2Signer sm2Signer = new SM2Signer();
		sm2Signer.init(false, parametersWithID);
		sm2Signer.update(message, 0, message.length);
		// 验证签名结果
		boolean verify = sm2Signer.verifySignature(signData);
		return verify;
	}

	/**
	 * 把64字节的纯R+S字节流转换成DER编码字节流
	 * 
	 * @param rawSign
	 * @return
	 * @throws IOException
	 */
	public static byte[] do签名DER编码(byte[] rawSign) throws IOException {
		// 要保证大数是正数
		BigInteger r = new BigInteger(1, extractBytes(rawSign, 0, 32));
		BigInteger s = new BigInteger(1, extractBytes(rawSign, 32, 32));
		ASN1EncodableVector v = new ASN1EncodableVector();
		v.add(new ASN1Integer(r));
		v.add(new ASN1Integer(s));
		return new DERSequence(v).getEncoded(ASN1Encoding.DER);
	}

	private static byte[] extractBytes(byte[] src, int offset, int length) {
		byte[] result = new byte[length];
		System.arraycopy(src, offset, result, 0, result.length);
		return result;
	}

	/**
	 * 由于大数的补0规则，所以可能会出现33个字节的情况，要修正回32个字节
	 * 
	 * @param 长度
	 * @param derSign
	 * @return
	 */
	public static byte[] do签名DER解码(int 长度, byte[] derSign) {
		ASN1Sequence as = DERSequence.getInstance(derSign);
		byte[] rBytes = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray();
		byte[] sBytes = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray();

		rBytes = fixToCurveLengthBytes(长度, rBytes);
		sBytes = fixToCurveLengthBytes(长度, sBytes);
		byte[] rawSign = new byte[rBytes.length + sBytes.length];
		System.arraycopy(rBytes, 0, rawSign, 0, rBytes.length);
		System.arraycopy(sBytes, 0, rawSign, rBytes.length, sBytes.length);
		return rawSign;
	}

	private static byte[] fixToCurveLengthBytes(int 长度, byte[] src) {
		// i
		if (src.length == 长度) {
			return src;
		}
		byte[] result = new byte[长度];
		if (src.length > 长度) {
			System.arraycopy(src, src.length - result.length, result, 0, result.length);
		} else {
			System.arraycopy(src, 0, result, result.length - src.length, src.length);
		}
		return result;
	}

	public static int getCurveLength(ECDomainParameters domainParams) {
		return (domainParams.getCurve().getFieldSize() + 7) / 8;
	}

	/**
	 * 将R或者S修正为固定字节数
	 *
	 * @param rs
	 * @return
	 */
	public static byte[] modifyRSFixedBytes(byte[] rs) {
		int length = rs.length;
		int fixedLength = 32;
		byte[] result = new byte[fixedLength];
		if (length < 32) {
			System.arraycopy(rs, 0, result, fixedLength - length, length);
		} else {
			System.arraycopy(rs, length - fixedLength, result, 0, fixedLength);
		}
		return result;
	}

	public static ECPrivateKeyParameters load私钥(String 私钥, ECDomainParameters 曲线) {
		if (!MyFun.isEmpty(私钥)) {

			BigInteger 私钥数 = new BigInteger(私钥, 16);
			return new ECPrivateKeyParameters(私钥数, 曲线);
		}
		return null;
	}

	public static ECPublicKeyParameters load公钥(String 公钥, ECDomainParameters 曲线) {
		if (!MyFun.isEmpty(公钥)) {

			ECPoint 公钥点 = 曲线.getCurve().decodePoint(Hex.decode(公钥));
			// 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
			return new ECPublicKeyParameters(公钥点, 曲线);
		}
		return null;
	}

	public static ECPrivateKeyParameters load私钥(String 私钥) {

		ECDomainParameters 曲线 = g生成曲线();
		return load私钥(私钥, 曲线);

	}

	public static ECPublicKeyParameters load公钥(String 公钥) {

		ECDomainParameters 曲线 = g生成曲线();
		return load公钥(公钥, 曲线);
	}

	public static ECDomainParameters g生成曲线() {
		// 获取一条SM2曲线参数
		X9ECParameters sm2ECParameters = GMNamedCurves.getByName(SM2_STD);
		// 构造domain参数
		ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(),
				sm2ECParameters.getN());
		return domainParameters;
	}

	/**
	 * 生成SM2公私钥对
	 *
	 * @return
	 */
	public static AsymmetricCipherKeyPair g生成公私钥对() throws NoSuchAlgorithmException {

		ECDomainParameters domainParameters = g生成曲线();
		// 创建密钥生成器
		ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
		// 初始化生成器,带上随机数
		keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
		// 生成密钥对
		AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
		return asymmetricCipherKeyPair;
	}

	public static SM2Engine.Mode g模式(String mode) {
		if (mode.contains("132")) {
			return SM2Engine.Mode.C1C3C2;
		} else {
			return SM2Engine.Mode.C1C2C3;
		}
	}

	public static String g模式(SM2Engine.Mode mode) {
		if (SM2Engine.Mode.C1C3C2 == mode) {
			return "132";
		} else {
			return "123";
		}
	}

	static Map<String, SM2> sm2库 = new ConcurrentHashMap<>();

	public static SM2 loadSM2(String 文件路径) {
		File file = new File(文件路径);
		String 库名 = file.getName();
		if (sm2库.containsKey(库名)) {
			return sm2库.get(库名);
		} else {
			SM2 sm= new SM2(file);
			sm2库.put(库名,sm);			
			return sm;
		}
	}


	public static void main(String[] args) throws InvalidCipherTextException {
		String pubkey = "0420b6aa10e86195819252ceee9504970ea14f06e5b346f340803256867199583b90ebb73e94e6951bcbe9ab537db61eabeb1c9a2ae922f0d8f1fd03ad4106b70b";
		String prikey = "35b694fbc559337ed1388180973d22e809f1aa67d59dee166f20a4168855dba7";
		var data = "顶顶顶顶!@^&*()_";
		String s16 = MySm2.do公钥加密(pubkey, data);
		MyFun.print(s16);
		//
		s16 = "f6845f2345e3509a5cd42f1577c7644e3233aed72b3679708a881787ff66a2eddd36eebad598a02a55e258cd80029002893af689c92e98c87001d400ac5b6daace915aef46da2b1c360fd1b7c2126acdbfd865b55cc05db0f422e854b8baeb8c5d1605096a6a0c503aeaeccc33a9a4d6d5d59bc7595fe73076e9ee279df44b8d";
		// 04cbda85f20a848213c5d22d675d7a001dc4be05564eb910993cd81a47ea96825de5314f87b536aff8722f493a3716f07680e1e3f36626cdda4f17c0835603befebde17e99b9924edc19d6646231b4af3e534fcc6b1b63bba5158622434
		// 04790812066afc75318311a017c5c24d7b8a57f94a72c6ab234425492ba2dd6b8e46d63d97a0bbf2ce0190168c592f2661c127a818278f05a5a5657720d9dbb49477a289d343b03a92ab13590cb6f52a1206914a131a1d860daf0ba6aa799e46fd3da3dc
		String j16 = MySm2.do私钥解密(prikey, s16);
		MyFun.print(j16);
		MyFun.print("74C37A2ABAECBBBC995D65E73894DF37");
		 
	}
}
